package network.websocket;

import config.AppConfig;
import contracts.ContractTypeSerializer;
import contracts.requests.LoginByTokenRequest;
import eventbus.PageEventBus;
import eventbus.PageEvents;
import org.apache.log4j.Logger;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import state.SessionState;
import util.DialogHelper;

import java.net.URI;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * ws客户端
 *
 * @author wyl
 */
public class WSClient {
    public static final WSClient Instance = new WSClient();
    private static final Logger logger = Logger.getLogger(WSClient.class);
    AtomicReference<WebSocketClient> client = new AtomicReference<>(null);
    private volatile boolean isRun = false;

    private void init() {
        client.set(createClient());
    }

    public WebSocketClient createClient() {
        String ws_serverUrl = AppConfig.getServerUrl();
        return new WebSocketClient(URI.create(ws_serverUrl), new Draft_6455()) {
            @Override
            public void onOpen(ServerHandshake serverHandshake) {
                logger.info("链接成功" + serverHandshake.getHttpStatusMessage());
                PageEventBus.publish(PageEvents.CONNECT_SUCCESS_EVENT);


                //已经为登录成功状态,但是服务器断线，尝试通过内存中的token重新登录
                if (SessionState.Instance.isLoginSuccess()) {
                    String token = SessionState.Instance.getToken();
                    if (!"".equals(token)) {
                        WSClientSender.send(new LoginByTokenRequest(token));
                    }
                }


            }

            @Override
            public void onMessage(String msg) {
                System.out.println("服务器发来消息:\n" + msg);
                WSDto wsDto = WSDtoConverter.readJsonToWSDto(msg);
                //WSDto转成响应实体
                Object response = ContractTypeSerializer.deserialize(wsDto.typeName, wsDto.dataJson);
                //请求处理器总线处理客户端请求
                PageEventBus.publish(response);
            }

            @Override
            public void onError(Exception e) {
                logger.error("出现异常" + e);
            }


            @Override
            public void onClose(int i, String s, boolean b) {
                logger.info("链接关闭");
                PageEventBus.publish(PageEvents.CONNECT_FAIL_EVENT);
            }
        };
    }

    public void startConnect() {
        if (!isRun) {
            isRun = true;
            ExecutorService service = Executors.newSingleThreadExecutor();
            service.execute(() -> {
                init();
                WebSocketClient c = client.get();
                logger.info("正在与服务器链接...");
                c.connect();
                inhibitionReconnect = false;
                isRun = false;
                service.shutdown();
            });
        }
    }

    private volatile boolean inhibitionReconnect = false;
    private int curReconnectCount = 0;
    private int maxReconnectCount = 5;
    ScheduledExecutorService service;

    /**
     * 开启自动重连监听
     * 监听轮询受抑制器影响
     */
    public void startAutoConnectListener() {
        if (service != null) {
            return;
        }
        service = Executors.newSingleThreadScheduledExecutor();
        service.scheduleAtFixedRate(() -> {
            try {
                WebSocketClient c = client.get();
                if (c != null && !inhibitionReconnect) {
                    if (!c.isOpen()) {
                        curReconnectCount++;
                        logger.info("正在进行第" + curReconnectCount + "次重连....");
                        c.reconnect();
                        if (curReconnectCount > maxReconnectCount) {
                            DialogHelper.showFailDialog(
                                    "与服务器链接失败\n" +
                                            "请检查配置文件(./res/config.ini)\n" +
                                            "如果地址正确,请联系服务器管理员qq:xxxxxxx");
                            System.exit(0);
                        }
                    } else {
                        curReconnectCount = 0;
                        logger.info("ws链接正常");
                        PageEventBus.publish(PageEvents.CONNECT_SUCCESS_EVENT);
                    }
                }
            } catch (Exception e) {
                logger.error("自动重连出现异常", e);
                service.shutdown();
            }
        }, 0, 3, TimeUnit.SECONDS);
    }

    public void closeConnect() {
        WebSocketClient c = client.get();
        if (c != null) {
            c.close();
        }
    }

    public void stopAutoConnectListener() {
        if (service != null) {
            service.shutdown();
            service = null;
        }
    }

    /**
     * 重新一次新的链接，释放当前链接开启下一次新的链接，主要是为了配置更改后重启
     * inhibitionReconnect = true 抑制重连监听器的作用，在新的链接开启后取消抑制
     */
    public void restartConnect() {
        inhibitionReconnect = true;
        closeConnect();
        startConnect();
    }
}
